<!--
 * @Author: cjy
 * @Date: 2023-05-08 17:06:21
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2023-05-09 17:01:46
 * @Descripttion: 
 * @FilePath: \vue-openlayers-demos\src\views\measurement\Measurement.vue
-->
<template>
  <div class="vm">
    <h2 class="h-title">测量</h2>
    <el-select v-model="type" placeholder="请选择测量类型" @change="typeChange">
      <el-option label="长度（线）" value="length"></el-option>
      <el-option label="面积（面）" value="area"></el-option>
      <el-option label="清除测量" value="clear"></el-option>
    </el-select>
    <div id="map" class="map-x"></div>
  </div>
</template>

<script>
import Draw from "ol/interaction/Draw.js";
import Map from "ol/Map.js";
import Overlay from "ol/Overlay.js";
import View from "ol/View.js";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style.js";
import { LineString, Polygon } from "ol/geom.js";
import { XYZ, Vector as VectorSource } from "ol/source.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { getArea, getLength } from "ol/sphere.js";
import { unByKey } from "ol/Observable.js";

export default {
  data() {
    return {
      type: "",
      measureTooltipElement: null,
      helpTooltipElement: null,
      helpTooltip: null,
      measureTooltip: null,
      source: null,
      sketch: null,
    };
  },
  mounted() {
    this.mapinit();
  },
  methods: {
    mapinit() {
      const raster = new TileLayer({
        source: new XYZ({
          url: "http://t0.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=4d314458b2e0a90a498c0ae62142c9fd",
        }),
      });
      const raster1 = new TileLayer({
        source: new XYZ({
            url: 'http://t6.tianditu.com/DataServer?T=cva_c&x={x}&y={y}&l={z}&tk=4d314458b2e0a90a498c0ae62142c9fd',
            projection: "EPSG:4326",
        })
      })

      this.source = new VectorSource();

      const vector = new VectorLayer({
        source: this.source,
        style: new Style({
            fill: new Fill({
                color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new Stroke({
                color: '#ffcc33',
                width: 2,
            })
        })
      });

      this.map = new Map({
        layers: [raster,raster1, vector],
        target: "map",
        view: new View({
          projection: "EPSG:4326",
          center: [111, 28],
          zoom: 6,
        }),
      });
      
    },
    /**
     * 鼠标移动事件
     * @param {import("../src/ol/MapBrowserEvent").default} evt The event.
     */
    pointerMoveHandler(evt) {
        if (evt.dragging) {
            return;
        }
        /** 
         * 开始测量提示
         * @type {string} */
        let helpMsg = "点击开始测量";

        /**
         * 画面的时候提示信息
         * @type {string}
         */
        const continuePolygonMsg = "点击继续画面测量";
        /**
         * 画线的时候提示信息
         * @type {string}
         */
        const continueLineMsg = "点击继续画线测量";

        if (this.sketch) {
            const geom = this.sketch.getGeometry();
            if (geom instanceof Polygon) {
            helpMsg = continuePolygonMsg;
            } else if (geom instanceof LineString) {
            helpMsg = continueLineMsg;
            }
        }

        this.helpTooltipElement.innerHTML = helpMsg;
        this.helpTooltip.setPosition(evt.coordinate);

        this.helpTooltipElement.classList.remove("hidden");
    },
     /**
     * 计算长度
     * @param {LineString} line 线要素
     * @return {string} 线长度
     */
    formatLength(line) {
        const length = getLength(line,{
            projection: 'EPSG:4326'
        });
        let output;
        if (length > 100) {
            output = Math.round((length / 1000) * 100) / 100 + " " + "km";
        } else {
            output = Math.round(length * 100) / 100 + " " + "m";
        }
        return output;
    },
    /**
     * 计算面积
     * @param {Polygon} polygon 面要素
     * @return {string} 面积
     */
    formatArea (polygon) {
        const area = getArea(polygon,{
            projection: 'EPSG:4326'
        });
        let output;
        if (area > 10000) {
            output =
            Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
        } else {
            output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
        }
        return output;
    },
     /**
     * 创建一个帮助信息提示
     */
    createHelpTooltip() {
        if (this.helpTooltipElement) {
            this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
        }
        this.helpTooltipElement = document.createElement("div");
        this.helpTooltipElement.className = "ol-tooltip hidden";
        this.helpTooltip = new Overlay({
            element: this.helpTooltipElement,
            offset: [15, 0],
            positioning: "center-left",
        });
        this.map.addOverlay(this.helpTooltip);
    },
    /**
     * 创建一个新的测量提示信息
     */
    createMeasureTooltip() {
      /**
       * 测量的提示信息元素
       * @type {HTMLElement}
       */
      if (this.measureTooltipElement) {
        this.measureTooltipElement.parentNode.removeChild(
          this.measureTooltipElement
        );
      }
      this.measureTooltipElement = document.createElement("div");
      this.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
      this.measureTooltip = new Overlay({
        element: this.measureTooltipElement,
        offset: [0, -15],
        positioning: "bottom-center",
        stopEvent: false,
        insertFirst: false,
      });
      this.map.addOverlay(this.measureTooltip);
    },

    // 测量交互方法
    addInteraction() {
      const type = this.type == "area" ? "Polygon" : "LineString";
      this.draw; // global so we can remove it later
      this.draw = new Draw({
        source: this.source,
        type: type,
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)",
          }),
          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2,
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)",
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)",
            }),
          }),
        }),
      });
      this.map.addInteraction(this.draw);

      this.createMeasureTooltip();   
      this.createHelpTooltip();

      //
      this.map.on("pointermove", this.pointerMoveHandler);
      this.map.getViewport().addEventListener("mouseout", () => {
        this.helpTooltipElement.classList.add("hidden");
      });

      let listener;
      this.draw.on("drawstart", (evt) => {
        this.sketch = evt.feature;
        let tooltipCoord = evt.coordinate;
        // 图形绘制监听
        listener = this.sketch.getGeometry().on("change", (evt) => {
          const geom = evt.target;
          let output;
          if (geom instanceof Polygon) {
            output = this.formatArea(geom);
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          } else if (geom instanceof LineString) {
            output = this.formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
          }
          this.measureTooltipElement.innerHTML = output;
          this.measureTooltip.setPosition(tooltipCoord);
        });
      });

      this.draw.on("drawend", () => {
        this.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        this.measureTooltip.setOffset([0, -7]);
        // 释放要素
        this.sketch = null;
        // 释放提示元素
        this.measureTooltipElement = null;
        this.createMeasureTooltip();
        unByKey(listener);
      });
    },
    // 测量类型切换
    typeChange() {
      this.map.removeInteraction(this.draw);
      this.addInteraction();
      // 清除测量信息
      if(this.type == 'clear'){
        this.source.clear(true)
        this.map.getOverlays().forEach(element => {
            this.map.removeOverlay(element)
        });
      }
    },
  },
};
</script>

<style>
.ol-tooltip {
  position: relative;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  color: white;
  padding: 4px 8px;
  opacity: 0.7;
  white-space: nowrap;
  font-size: 12px;
  cursor: default;
  user-select: none;
}
.ol-tooltip-measure {
  opacity: 1;
  font-weight: bold;
}
.ol-tooltip-static {
  background-color: #ffcc33;
  color: black;
  border: 1px solid white;
}
.ol-tooltip-measure:before,
.ol-tooltip-static:before {
  border-top: 6px solid rgba(0, 0, 0, 0.5);
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  content: "";
  position: absolute;
  bottom: -6px;
  margin-left: -7px;
  left: 50%;
}
.ol-tooltip-static:before {
  border-top-color: #ffcc33;
}
</style>